import java.io.FileOutputStream;
import java.util.Map;
import java.util.Properties;

/**
 * Java程序在不同操作系统上运行时，可能需要取得平台相关的属性，或者调用平台命令来完成特定功能。
 * Java提供了System类和Runtime类来与程序的运行平台进行交互。
 * <p>
 * System类代表当前Java程序的运行平台，程序不能创建System类的对象，System类提供了一些类变量和类方法，允许直接通过System类来调用这些类变量和类方法。
 * System类提供了代表标准输入、标准输出和错误输出的类变量，并提供了一些静态方法用于访问环境变量、系统属性的方法，还提供了加载文件和动态链接库的方法。
 * 下面程序通过System类来访问操作的环境变量和系统属性。
 * <p>
 * 注意：加载文件和动态链接库主要对native方法有用，对于一些特殊的功能（如访问操作系统底层硬件设备等）Java程序无法实现，
 * 必须借助C语言来完成，此时需要使用C语言为Java方法提供实现。
 * 其实现步骤如下：①Java程序中声明native修饰的方法，类似于abstract方法，只有方法签名，没有实现。
 * 使用带-h选项的javac命令编译该Java程序，将生成一个.class文件和一个.h头文件。
 * ②写一个.cpp文件实现native方法，这一步需要包含第1步产生的.h文件（这个.h文件中又包含了JDK带的jni.h文件）。
 * ③将第2步的.cpp文件编译成动态链接库文件。
 * ④在Java中用System类的loadLibrary..()方法或Runtime类的loadLibrary()方法加载第3步产生的动态链接库文件，Java程序中就可以调用这个native方法了。
 * <p>
 * 注意：在Java 9以前，javac命令没有-h选项，因此JDK提供了javah命令来为.class文件生成.h头文件。
 * Java 10彻底删除了javah命令，javac的-h选项代替了javah。
 */
class SystemTest {
    static void test() throws Exception {
        // 获取系统所有的环境变量
        Map<String, String> env = System.getenv();
        for (String name : env.keySet()) {
            System.out.println(name + "--->" + env.get(name));
        }
        // 获取指定环境变量的值
        System.out.println(System.getenv("JAVA_HOME"));
        // 获取所有的系统属性
        Properties properties = System.getProperties();
        // 将所有的系统属性保存到 properties.txt文件中
        properties.store(new FileOutputStream(" properties.txt"), "System Properties");
        // 输出特定的系统属性
        System.out.println(System.getProperty("os.name"));
    }
}

/**
 * System类还有两个获取系统当前时间的方法：currentTimeMillis()和nanoTime()，它们都返回一个long型整数。
 * 实际上它们都返回当前时间与UTC1970年1月1日午夜的时间差，前者以毫秒作为单位，后者以纳秒作为单位。
 * 必须指出的是，这两个方法返回的时间粒度取决于底层操作系统，可能所在的操作系统根本不支持以毫秒、纳秒作为计时单位。
 * 例如，许多操作系统以几十毫秒为单位测量时间，currentTimeMillis()方法不可能返回精确的毫秒数；
 * 而nanoTime()方法很少用，因为大部分操作系统都不支持使用纳秒作为计时单位。
 * <p>
 * 除此之外，System类的in、out和err分别代表系统的标准输入（通常是键盘）、标准输出（通常是显示器）和错误输出流，
 * 并提供了setIn()、setOut()和setErr()方法来改变系统的标准输入、标准输出和标准错误输出流。
 * <p>
 * System类还提供了一个identityHashCode(Object x)方法，该方法返回指定对象的精确hashCode值，也就是根据该对象的地址计算得到的hashCode值。
 * 当某个类的hashCode()方法被重写后，该类实例的hashCode()方法就不能唯一地标识该对象；
 * 但通过identityHashCode()方法返回的hashCode值，依然是根据该对象的地址计算得到的hashCode值。
 * 所以，如果两个对象的identityHashCode值相同，则两个对象绝对是同一个对象。如下程序所示。
 */
class IdentityHashCodeTest {
    static void test() {
        String s1 = new String("Hello");
        String s2 = new String("Hello");
        // String重写了hashCode()方法
        // 因为s1和s2的字符序列相同，所以他们的hashCode()方法返回值相同
        System.out.println(s1.hashCode() + "---" + s2.hashCode());
        // s1和s2是不同的字符串对象，所以他们的identityHashCode值不同
        System.out.println(System.identityHashCode(s1) + "---" + System.identityHashCode(s2));
        String s3 = "Java";
        String s4 = "Java";
        // s3和s4是相同的字符串对象，所以他们的identityHashCode值相同
        System.out.println(System.identityHashCode(s3) + "----" + System.identityHashCode(s4));
    }
}

/**
 * Runtime类与Java 9的ProcessHandle
 * Runtime类代表Java程序的运行时环境，每个Java程序都有一个与之对应的Runtime实例，应用程序通过该对象与其运行时环境相连。
 * 应用程序不能创建自己的Runtime实例，但可以通过getRuntime()方法获取与之关联的Runtime对象。
 * 与System类似的是，Runtime类也提供了gc()方法和runFinalization()方法来通知系统进行垃圾回收、清理系统资源，
 * 并提供了load(String filename)和loadLibrary(String libname)方法来加载文件和动态链接库。
 * Runtime类代表Java程序的运行时环境，可以访问JVM的相关信息，如处理器数量、内存信息等。
 * 如下程序所示。
 */
class RuntimeTest {
    static void test() {
        Runtime rt = Runtime.getRuntime();
        System.out.println("处理器数量：" + rt.availableProcessors());
        System.out.println("空闲内存数：" + rt.freeMemory());
        System.out.println("总内存数“：" + rt.totalMemory());
        System.out.println("可用最大内存数：" + rt.maxMemory());
    }
}

/**
 * 上面程序中粗体字代码就是Runtime类提供的访问JVM相关信息的方法。
 * 除此之外，Runtime类还有一个功能——它可以直接单独启动一个进程来运行操作系统的命令，如下程序所示。
 */
class ExecTest {
    static void test() throws Exception {
        Runtime rt = Runtime.getRuntime();
        rt.exec("notepad.exe");
    }
}

/**
 * 通过exec启动平台上的命令之后，它就变成了一个进程，Java使用Process来代表进程。
 * Java 9还新增了一个ProcessHandle接口，通过该接口可获取进程的ID、父进程和后代进程；
 * 通过该接口的onExit()方法可在进程结束时完成某些行为。
 * ProcessHandle还提供了一个ProcessHandle.Info类，用于获取进程的命令、参数、启动时间、累计运行时间、用户等信息。
 * 下面程序示范了通过ProcessHandle获取进程的相关信息。
 */
class ProcessHandleTest {
    static void test() throws Exception {
        Runtime rt = Runtime.getRuntime();
        Process p = rt.exec("notepad.exe");
//        ProcessHandle ph = p.toHandle();
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        SystemTest.test();
        IdentityHashCodeTest.test();
        ExecTest.test();
        RuntimeTest.test();
        ProcessHandleTest.test();
    }
}
